(function(angular) {
	'use strict';
	angular.module('fs.router', [ 'fs.logger', 'fs.storage', 'ui.router', 'oc.lazyLoad' ])
	.provider('routerService', routerService)
	.config(configuration)
	.run(main);
	routerService.$inject = [ '$stateProvider', '$urlRouterProvider', '$locationProvider'];
	function routerService($stateProvider, $urlRouterProvider, $locationProvider) {
		var provider = this;
		provider.theme = 'default';
		provider.multipleViews = {};
		provider.cacheLayout = false;
		provider.cacheResource = false;
		provider.cacheAuthorization = false;
		provider.cacheValidationRule = false;
		provider.lazyLoad = lazyLoad;
		provider.preLoadLayoutConfig = preLoadLayoutConfig;
		provider.preLoadValidatorConfig = preLoadValidatorConfig;
		provider.preLoadI18nConfig = preLoadI18nConfig;
		provider.preLoadAuthorizationConfig = preLoadAuthorizationConfig;
		provider.setTheme = function(theme) {
			provider.theme = theme;
		};
		provider.getTheme = function() {
			return provider.theme;
		};
		provider.setHomeUrl = function(homeUrl) {
			$urlRouterProvider.otherwise(homeUrl);
		};
		provider.setStates = function(stateConfig) {
			angular.forEach(stateConfig, function(value, key) {
				if (value.views) {
					provider.multipleViews[key.split('.').join('_')] = key;
				}
				this.state(key, value);
			}, $stateProvider);
		};
		provider.getTemplateUrl = function (templateUrl) {
			return templateUrl.replace('${theme}', provider.theme);
		};
		provider.setSimpleStates = function(simpleStates, module) {
			var states = {};
			simpleStates = flattenSimpleStateConfig(simpleStates);
			angular.forEach(simpleStates, function(value, key) {
				if (key.indexOf('^') == -1) {
					simpleStates[module + '_' + key] = value;
					delete simpleStates[key];
				} else {
					simpleStates[key.substr(1)] = value;
					delete simpleStates[key];
				}
			});
			angular.forEach(simpleStates, function(stateConfig, stateName) {
				stateConfig = stateConfig || {};
				if (!this[stateName] && !stateConfig.__ignore) {
					stateConfig.module = module;
					this[stateName] = buildState(stateName.split('.').join('_'), stateConfig);
				}
			}, states);
			try {
				provider.setStates(states);
			} catch (e) {
				provider.log.debug(e);
			}
		};
		
		getter.$inject = ['storageService', 'StorageConstants', '$log'];
		function getter(storageService, StorageConstants, $log) {
			provider.storageService = storageService;
			provider.Constants = StorageConstants;
			provider.log = $log.getInstance('routerService');
			return {
				setTheme : provider.setTheme,
				getTheme : provider.getTheme,
				parseState : parseState
			};
		}
		provider.$get = getter;
		
		function parseState(state) {
			if (state.charAt(0) == '^') {
				state = state.replace('^', '');
			} else if (provider.multipleViews[state.split('.').join('_')]) {
				state = provider.multipleViews[state.split('.').join('_')];
			} else {
				state = state.split('.').join('_');
			}
			if (state.indexOf('poi_') != 0) {
				state = 'poi_' + state;
			}
			return state;
		}
		function lazyLoad(files) {
			angular.forEach(files, function(file, index) {
				this[index] = file.replace('${theme}', provider.theme);
			}, files);
			return [ '$ocLazyLoad', '$stateParams', function($ocLazyLoad, $stateParams) {
				return $ocLazyLoad.load(files, {
					cache : true
				});
			} ];
		}
		function buildState(stateName, stateConfig, isMultipleViews) {
			var state = {
				url : '/' + (stateConfig.views ? stateName.split('_').pop() : stateName.split('_').join('/')),
				resolve : getResolve(stateName, stateConfig),
				params : {
					params : stateConfig.params
				}
			};
			state.url = state.url.replace(stateConfig.module + '/', '');
			if (isMultipleViews) {
				state.controller = stateConfig.controller || getController(stateName, stateConfig.__inheritController);
				state.controllerAs = stateConfig.controllerAs || 'vm';
			} else {
				state.controllerProvider = stateConfig.controllerProvider || getControllerProvider(stateName, stateConfig.__inheritController);
			}
			if (stateConfig.template) {
				state.template = stateConfig.template;
			} else {
				state.templateUrl = getTemplateUrl(stateName, stateConfig.templateUrl, stateConfig.module);
			}
			if (stateConfig.views) {
				state.views = { };
				angular.forEach(stateConfig.views, function(view, key) {
					state.views[key] = buildState(stateName, view, true);
					delete state.views[key].url;
					delete state.views[key].params;
					delete state.views[key].resolve;
				});
			}
			return state;
		}
		function getResolve(stateName, stateConfig) {
			return {
				dependentFile : preLoadDependFile(stateName, stateConfig.__inheritController, stateConfig.__dependentFiles, stateConfig.module),
				layoutConfig : preLoadLayoutConfig(stateConfig.__layoutConfig, stateName, stateConfig.module),
				validatorConfig : preLoadValidatorConfig(stateConfig.__validatorConfig, stateName, stateConfig.module),
				i18nConfig : preLoadI18nConfig(stateConfig.__i18nConfig, stateName, stateConfig.module),
				authService : preLoadAuthorizationConfig(stateConfig.__authorizationConfig, stateName, stateConfig.module),
			};
		}
		function getTemplateUrl(stateName, templateUrl, module) {
			if (templateUrl) {
				return templateUrl = templateUrl.replace('${theme}', provider.theme);
			}
			return function($stateParams) {
				var fileName, prefix, path;
				path = stateName.split('_');
				if (path.length > 2) {
					fileName = path.pop();
					prefix = path.pop();
					fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
					path.push(prefix);
					path.push(fileName);
				}
				if(path.length > 1) path.shift();
				templateUrl = 'resources/' + provider.theme + '/nux/view/' + path.join('/') + '.html';
				return templateUrl;
			};
		}
		function getController(stateName, inheritController) {
			var controller, stateNameArray;
			stateNameArray = stateName.split('_');
			if (inheritController === true) {
				stateNameArray.pop();
			}
			controller = stateNameArray.join('.') + 'Controller';
			return controller;
		}
		function getControllerProvider(stateName, inheritController) {
			controllerProvider.$inject = [ '$state', '$stateParams' ];
			function controllerProvider($state, $stateParams) {
				var controller, stateNameArray = stateName.split('_');
				if (inheritController === true) {
					stateNameArray.pop();
				}
				controller = stateNameArray.join('.') + 'Controller as vm';
				return controller;
			}
			return controllerProvider;
		}
		function preLoadDependFile(stateName, inheritController, files, module) {
			dependentFile.$inject = [ '$ocLazyLoad', '$q', '$state', '$log' ];
			function dependentFile($ocLazyLoad, $q, $state, $log) {
				var filePathArray, fileName, prefix, defer = $q.defer();
				var dependentFiles = angular.copy(files) || [];
				angular.forEach(dependentFiles, function(filePath, index) {
					dependentFiles[index] = filePath.replace('${theme}', provider.theme);
				});
				filePathArray = stateName.split('_');
				if (filePathArray.length > 1 && inheritController === true) {
					filePathArray.pop();
				} else if (filePathArray.length > 2) {
					fileName = filePathArray.pop();
					prefix = filePathArray.pop();
					fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
					filePathArray.push(prefix);
					filePathArray.push(fileName);
				}
				if(filePathArray.length > 1) filePathArray.shift();
				dependentFiles.push('resources/' + provider.theme + '/nux/view/' + filePathArray.join('/') + 'Controller.js');
				$ocLazyLoad.load(dependentFiles, {
					cache : true
				}).then(function(e){
					defer.resolve();
				},function(e){
					defer.resolve();
				});
				return defer.promise;
			}
			return dependentFile;
		}
		function preLoadLayoutConfig(url, stateName, module) {
			layoutConfig.$inject = [ '$rootScope', '$q', '$stateParams', '$state', 'Constants', 'ajaxServiceFactory', 'storageService' ];
			function layoutConfig($rootScope, $q, $stateParams, $state, Constants, ajaxServiceFactory, storageService) {
				var ajaxService, siteInfo, defer = $q.defer(), layoutUrl;
				if (!url && stateName) {
					url = stateName.split('_');
					url = url.slice(-2);
					url = 'layout/' + url.join('/') + ".do";
				}
				if (url) {
					siteInfo = storageService.getSiteInfo();
					layoutUrl = url;
					if ($stateParams.params && $stateParams.params.siteCode && siteInfo && siteInfo.siteCode && $stateParams.params.siteCode.substr(0, 6) != siteInfo.siteCode) {
						layoutUrl = layoutUrl + "/" + $stateParams.params.siteCode.substr(0, 6);
					}
					ajaxService = ajaxServiceFactory.buildAjaxService({
						loadConfig : {
							'url' : layoutUrl,
							'method' : 'get',
							'cache' : provider.cachelayout
						}
					});
					ajaxService.loadConfig().then(function(response) {
						defer.resolve(response ? response.data : {});
					}, function(error) {
						log.debug('can not load config[' + layoutUrl + ']:' + error);
						defer.reject();
					});
				} else {
					defer.resolve({});
				}
				return defer.promise;
			}
			return layoutConfig;
		}
		function preLoadValidatorConfig(url, stateName, module) {
			validatorConfig.$inject = [ '$rootScope', '$q', '$state', 'Constants', 'storageService', 'ajaxServiceFactory' ];
			function validatorConfig($rootScope, $q, $state, Constants, storageService, ajaxServiceFactory) {
				var ajaxService, defer = $q.defer();
				if (!url && stateName) {
					url = stateName.split('_');
					url = url.slice(-2);
					url = 'validator/' + url.join('/') + ".do";
				}
				if (url) {
					ajaxService = ajaxServiceFactory.buildAjaxService({
						loadConfig : {
							'url' : url,
							'method' : 'get',
							'cache' : provider.cacheValidationRule
						}
					});
					ajaxService.loadConfig().then(function(response) {
						var resources = (response && response.data) ? response.data.resources : undefined;
						var rule = (response && response.data) ? response.data.rules : null;
						if (resources) {
							storageService.setItemToSession(Constants.SESSION_I18N_RESOURES, resources);
							$rootScope.$emit(Constants.NOTIFY_CHANGE_LANGUAGE);
						}
						defer.resolve(rule);
					}, function error(error) {
						log.debug('can not load config[' + url + ']:' + error);
						defer.reject();
					});
				} else {
					defer.resolve({});
				}
				return defer.promise;
			}
			return validatorConfig;
		}
		function preLoadI18nConfig(url, stateName, module) {
			i18nConfig.$inject = [ '$rootScope', '$q', '$state', 'Constants', 'storageService', 'ajaxServiceFactory' ];
			function i18nConfig($rootScope, $q, $state, Constants, storageService, ajaxServiceFactory) {
				var ajaxService, defer = $q.defer();
				if (!url && stateName) {
					url = stateName.split('_');
					url = url.slice(-2);
					url = 'resource/' + url.join('/') + ".do";
				}
				if (url) {
					ajaxService = ajaxServiceFactory.buildAjaxService({
						loadConfig : {
							'url' : url,
							'method' : 'get',
							'cache' : provider.cacheResource
						}
					});
					ajaxService.loadConfig().then(function(response) {
						var resources = response ? response.data : undefined;
						if (resources) {
							storageService.setItemToSession(Constants.SESSION_I18N_RESOURES, resources);
							$rootScope.$emit(Constants.NOTIFY_CHANGE_LANGUAGE);
						}
						defer.resolve(resources);
					}, function error(error) {
						log.debug('can not load config[' + url + ']:' + error);
						defer.reject();
					});
				} else {
					defer.resolve({});
				}
				return defer.promise;
			}
			return i18nConfig;
		}
		function preLoadAuthorizationConfig(url, stateName, module) {
			authorizationConfig.$inject = [ '$rootScope', '$q', '$stateParams', '$state', 'Constants', 'ajaxServiceFactory' ];
			function authorizationConfig($rootScope, $q, $stateParams, $state, Constants, ajaxServiceFactory) {
				var ajaxService, defer = $q.defer();
				if (!url && stateName) {
					url = stateName.split('_');
					url = url.slice(-2);
					url = 'authorization/' + url.join('/') + ".do";
				}
				if (url) {
					ajaxService = ajaxServiceFactory.buildAjaxService({
						loadConfig : {
							'url' : url,
							'method' : 'get',
							'cache' : provider.cacheAuthorization
						}
					});
					ajaxService.loadConfig().then(function(response) {
						$stateParams.authorization = buildAuthService($rootScope, Constants, ajaxServiceFactory, response ? response.data : {});
						defer.resolve($stateParams.authorization);
					}, function error(error) {
						log.debug('can not load config[' + url + ']:' + error);
						defer.reject();
					});
				} else {
					defer.resolve({});
				}
				return defer.promise;
			}
			return authorizationConfig;
		}
		function buildAuthService($rootScope, Constants, ajaxServiceFactory, authorizationConfig) {
			var authorizationServices = {}, authService, emptyServiceNames = [];
			angular.forEach(authorizationConfig, function(value, key) {
				if (value == '') {
					emptyServiceNames.push(key);
				} else {
					authorizationServices[key] = {
						url : value,
						method : 'post',
						'cache' : provider.cacheAuthorization
					};
				}
			});
			authService = ajaxServiceFactory.buildAjaxService(authorizationServices) || {};
			angular.forEach(emptyServiceNames, function(value, key) {
				authService[value] = angular.noop;
			});
			return authService;
		}
		function isLeafConfig(simpleState) {
			return !simpleState
					|| (simpleState.hasOwnProperty('views') || simpleState.hasOwnProperty('templateUrl') 
						|| simpleState.hasOwnProperty('__multipleViews') || simpleState.hasOwnProperty('__dependentFiles') 
						|| simpleState.hasOwnProperty('__inheritController') || simpleState.hasOwnProperty('__i18nConfig') 
						|| simpleState.hasOwnProperty('__layoutConfig') || simpleState.hasOwnProperty('__validatorConfig') 
						|| simpleState.hasOwnProperty('__authorizationConfig'));
		}
		function flattenSimpleStateConfig(simpleState) {
			var keys = {
				'views' : null,
				'templateUrl' : null,
				'__multipleViews' : null,
				'__dependentFiles' : null,
				'__i18nConfig' : null,
				'__layoutConfig' : null,
				'__validatorConfig' : null,
				'__authorizationConfig' : null,
				'__inheritController' : null,
				'__ignore' : null
			}, config = {}, tempConfig;
			angular.forEach(simpleState, function(value, key) {
				if (isLeafConfig(value)) {
					config[key] = value || {};
					if (value && value.__multipleViews) {
						tempConfig = flattenSimpleStateConfig(value);
						angular.forEach(tempConfig, function(v, k) {
							config[key + '.' + k] = v;
						});
					}
				} else if (!keys.hasOwnProperty(key)) {
					if (simpleState.__dependentFiles && simpleState.__dependentFiles.length > 0) {
						value.__dependentFiles = (value.__dependentFiles || []).concat(simpleState.__dependentFiles);
					}
					if (simpleState.params) {
						value.params = angular.merge((value.params || {}), simpleState.params);
					}
					tempConfig = flattenSimpleStateConfig(value);
					angular.forEach(tempConfig, function(v, k) {
						if (value.__multipleViews) {
							config[key + '.' + k] = v;
						} else {
							config[key + '_' + k] = v;
						}
					});
				}
			});
			return config;
		}
	}
	
	configuration.$inject = [ '$locationProvider', '$urlRouterProvider', 'routerServiceProvider' ];
	function configuration($locationProvider, $urlRouterProvider, routerServiceProvider) {
		// routerServiceProvider.setHomeUrl("/app/home");
		// $urlRouterProvider.deferIntercept(false);
		// $locationProvider.hashPrefix('');
		// $locationProvider.html5Mode({ enabled: false, requireBase: false, rewriteLinks: true });
	}

	main.$inject = [ '$rootScope', '$state', '$stateParams', '$window', 'loggerFactory', 'storageService' ];
	function main($rootScope, $state, $stateParams, $window, loggerFactory, storageService) {
		var log = loggerFactory.getLogger('router');
		$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
			log.debug(event.name + ': go[' + toState.name + ',' + toParams.params + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params)+ ']');
		});

		$rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams) {
			log.error(event.name + ': go[' + unfoundState.to + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']');
		});

		$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
			var pageId = toState.name ? toState.name.split('_').join('.') : '';
			if (pageId.indexOf('poi.menu.submenu') == 0) {
				if (toParams.params && toParams.params.displayName) {
					pageId = 'poi.menu.submenu.' + toParams.params.displayName;
				}
			}
			$rootScope.pageId = pageId;
			$rootScope.removePageDescription();
			$rootScope.removeFormState();
			$rootScope.saveAuditAndNoteParams(toState.name, toParams);
			$rootScope.saveHistory(event, toState, toParams, fromState, fromParams);
			log.debug(event.name + ': go[' + toState.name + ',' + angular.toJson(toParams.params) + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']');
		});

		$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
			log.error(event.name + ': go[' + toState.name + ',' + angular.toJson(toParams.params) + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']: ' + error);
		});
		
	}
})(angular);
